import os
from datetime import datetime, timedelta

from jose import jwt
from model.user import User

if os.getenv("CRYPTID_UNIT_TEST"):
    from fake import user as data
else:
    from data import user as data

#
# Auth-related stuff
#
from passlib.context import CryptContext

SECRET_KEY = "change-it-in-production!"
ALGORITHM = "HS256"
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


def _verify_password(plain: str, hash_from_db: str) -> bool:
    """Hash `plain` and compare with `hash_from_db`"""
    return pwd_context.verify(plain, hash_from_db)


def _get_hash(plain: str) -> str:
    """Return the hash of a `plain` string"""
    return pwd_context.hash(plain)


def _get_jwt_username(token: str) -> str | None:
    """Extract username from JWT access `token`"""
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        if not (username := payload.get("sub")):
            return None
    except jwt.JWTError:
        return None
    return username


def _lookup_user(username: str) -> User | None:
    """Return matching User from db for `name`"""
    if user := data.get_one(username):
        return user
    return None


def _get_current_user(token: str) -> User | None:
    """Decode and OAuth access `token` and return the User"""
    if not (username := _get_jwt_username(token)):
        return None
    if user := _lookup_user(username):
        return user
    return None


def auth_user(name: str, plain: str) -> User | None:
    """Authenticate user with `name` and `plain` password"""
    if not (user := _lookup_user(name)):
        return None
    if not _verify_password(plain, user.hash):
        return None
    return user


def create_access_token(data: dict, expires: timedelta | None = None) -> str:
    """Return a JWT access token created from `data`"""
    src = data.copy()
    now = datetime.now(datetime.UTC)
    if not expires:
        expires = timedelta(minutes=15)
    src["exp"] = now + expires
    return jwt.encode(src, SECRET_KEY, algorithm=ALGORITHM)


#
# CRUD-related stuff
#
def get_all() -> list[User]:
    return data.get_all()


def get_one(name: str) -> User:
    return data.get_one(name)


def create(user: User) -> User:
    return data.create(user)


def modify(name: str, user: User) -> User:
    return data.modify(name, user)


def delete(name: str) -> None:
    return data.delete(name)
